//////////////////////////////////////////////////////////////////////////
//
// This file is a part of XORT project.
//
// PROJECT:         Xort compiler execution subsystem
// FILE:            xruntime.h
// AUTHOR:          Vladimir Gumenuk
// DATE:            7-Dec-2012
// MODIFICATIONS:
//
//
//     Copyright(c) 2014, Vladimir Gumenuk
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification, 
//     are permitted provided that the following conditions are met :
//
//     1. Redistributions of source code must retain the above copyright notice, 
//        this list of conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
//        the following disclaimer in the documentation and / or other materials provided with the distribution.
//     3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
//        or promote products derived from this software without specific prior written permission.
//
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
//     THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
//     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#pragma once

/*  XVM stack frame
    -------------------------------------
    | 1 byte | 1 byte | 8 bytes         |
    |------------------------------------
    |  arg#  | 0x80   |  this  |  asp   |  <-- system frame, created by XVM for each procedure call
    -------------------------------------
        ^        ^        ^        ^
        |        |        |        |_ address of parent frame, or 0 for first frame
        |        |        |_ this pointer or NULL for static procedures
        |        |_ system marker
        |_ number of arguments passed into procedure or 0

    -------------------------------------
    |  0xFF  | 0x40   |        | handler| <-- system scope frame marker
    -------------------------------------
        ^        ^        ^        ^
        |        |        |        |_ address of parent frame, or 0 for first frame
        |        |        |_ reserved must be 0
        |        |_ system scope marker
        |_ reserved must be 0xFF

    -------------------------------------
    |  type  | access |       data      |  <-- normal user variable
    -------------------------------------
                 ^
                 |_ access flags: 0x03 - r/w, 0x01 - r-only
*/

#define XRTF_GOTO_EXECUTED 0x00000001

class XRuntime;
class XortJit64;

//////////////////////////////////////////////////////////////////////////
// Interface to debugger
class XRuntimeDebugEvents
{
public:
    virtual ~XRuntimeDebugEvents() { ; }
    virtual int onEnterSystemFrame( XRuntime *pRT, SymbolRef CurrentSymbol ) = 0;
    virtual int onLeaveSystemFrame( XRuntime *pRT, SymbolRef CurrentSymbol ) = 0;
    virtual int onEnterFrame( XRuntime *pRT ) = 0;
    virtual int onLeaveFrame( XRuntime *pRT ) = 0;
    virtual int beforeExecPrimitive( XRuntime *pRT, const InstructionData& primInfo ) = 0;
    virtual int afterExecPrimitive( XRuntime *pRT, const InstructionData& primInfo, int iLastError ) = 0;
    virtual int beforeExecBuildinProc( XRuntime *pRT ) = 0;
    virtual int afterExecBuildinProc( XRuntime *pRT, int iLastError ) = 0;
    virtual int beforeExecNativeProc( XRuntime *pRT ) = 0;
    virtual int afterExecNativeProc( XRuntime *pRT, int iLastError ) = 0;
    virtual int internalXVMError( XRuntime *pRT, int iError ) = 0;
    virtual void outputDebugMessage( const char* lpszMessage ) = 0;
};

//////////////////////////////////////////////////////////////////////////
// Xort runtime engine: XVM - Xort Virtual Machine
class XRuntime
{
public:
    XRuntime();
    virtual ~XRuntime();

    int Initialize( SYMBOLLIST symList, STRINGARRAY litPool, XRuntimeDebugEvents *pEventsSink, bool bJitEnabled );

    virtual int Run( SymbolRef entryPoint, int argc, char *argv[] );
    virtual int Exec( SymbolRef entryPoint );
    void        Reset(); // resets XVM to initial state after internal error. After this method Run or Exec can be called again

    inline SYMBOLLIST    get_GlobalSymbolList() { return *m_pSymList; }
    inline stringref     get_StringFromPool( dword dwIndex ) { return m_pLitPool->get( dwIndex ); }
    inline symbolref     get_CurrentProc() { return m_pCurrentProc; }
    inline dword         get_IP() const { return m_dwIP; }
    inline dword         get_Flags() const { return m_dwFLAGS; }
    inline XCELLSARRAY&  get_Stack() { return m_Stack; }
    inline XCELLSARRAY&  get_ReturnStack() { return m_RetStack; }
    inline dword         get_StackBase() const { return m_dwStackBase; }
    inline XCELLSARRAY&  get_Data() { return m_Data; }
    inline XortElement&  get_DataCell( dword dwIndex ) { return m_Data.get( dwIndex ); }
    inline SYMBOLSARRAY& get_Functions() { return m_Functions; }
         
    symbolref            get_FunctionByIndex( dword dwIdx );
    XortElement*         get_GlobalVariableByIndex( dword dwIdx );

    // statistics
    inline dword         get_ExecutedPrimsCount() const { return m_dwExecutedPrimsCount; }
    inline dword         get_MaxStackAllocatedDepth() const { return m_Stack.allocated(); }
    inline dword         get_MaxRecursionDepth() const { return m_dwMaxRecursionDepth; }
    inline const dword*  get_PrimitivesCounters() const { return &m_dwExecPrimsCounter[0]; }

    inline int           isDebuggerAttached() const { return m_pDbgEvents ? TRUE : FALSE; }

    void                 OutputDebugMessage( const char* lpszText ) { if( m_pDbgEvents ) m_pDbgEvents->outputDebugMessage( lpszText ); }

    // Create and initialize xort object, built-in object or external object from third-party dll
    int                  createXObject( SymbolRef objType, XortNativeInterface *pExternalObj, xobject **ppOut );

protected:
    int  InitInternal();
    int  exec_primitive( const InstructionData& primData );
    int  exec_primitive_fast( const InstructionData& primData );
    void DumpStack();
    void DebugMessage( const char *lpszMsg, ... );

    typedef int FASTCALL _opfn_t( XELEMENT x );
    int do_unary_prim( const InstructionData& primData, _opfn_t *pfn );

    typedef int FASTCALL _lopfn_t( XELEMENT v1, XELEMENT v2, XELEMENT vRes );
    int do_logical_op( const InstructionData& primData, _lopfn_t *pfn );
    int do_if_xx( const InstructionData& primData, _lopfn_t *pfn );

    typedef int FASTCALL _copfnifx( XELEMENT x, int& iResult );
    int do_ifz_nz( const InstructionData& primData, _copfnifx *pfn );

    int do_arith( const InstructionData& primData, _lopfn_t *pfn );

    inline void applyOffsetIP( int iOffset ) {
        if( m_pCurrentProc->m_dwFlags & f_instr_decoded ) {
            m_dwIP      = (dword)iOffset;
            m_dwFLAGS  |= XRTF_GOTO_EXECUTED;
        } else {
            int iIP     = (int)m_dwIP;
            int iTarget = iIP + iOffset;
            m_dwIP      = (dword)iTarget;
            m_dwFLAGS  |= XRTF_GOTO_EXECUTED;
        }
    }

    SYMBOL_LIST    *m_pSymList;    // reference to global symbol list
    STRING_ARRAY   *m_pLitPool;    // reference to literal pool

    SYMBOLSARRAY    m_Functions;   // global functions

    XCELLSARRAY     m_RetStack;    // return stack to store current IP ...and maybe some additional info in the future
    XCELLSARRAY     m_Stack;       // program stack
    XCELLSARRAY     m_Data;        // global variables
    dword           m_dwIP;        // instruction pointer
    dword           m_dwStackBase; // current stack base
    dword           m_dwFLAGS;     // execution flags
    dword           m_dwExecutedPrimsCount; // some statistics
    dword           m_dwCallsRecursionDepth;
    dword           m_dwMaxRecursionDepth;

#define PRIMS_COUNTER_SIZE 0x100
    dword           m_dwExecPrimsCounter[PRIMS_COUNTER_SIZE]; // counter for each primitive

    symbolref       m_pCurrentProc;    // procedure that is currently being executed
    XRuntimeDebugEvents *m_pDbgEvents; // debugger interface, can be NULL

    string         *m_pcsRuntimeName;  // fix first run-time initializers

    bool            m_bJitEnabled;     // true if xvm was invoked with /jit parameter
    XortJit64      *m_pJIT;            // jit code generator

    //////////////////////////////////////////////////////////////////////////
    // primitives
public: // public? strange yeah?
    int do_addrof( const InstructionData& primData ); 
    int do_load( const InstructionData& primData );   
    int do_mov( const InstructionData& primData );    
    int do_store( const InstructionData& primData );  
    int do_drop( const InstructionData& primData );   
    int do_dup( const InstructionData& primData );    
    int do_swap( const InstructionData& primData );   
    int do_nz( const InstructionData& primData );     
    int do_convert( const InstructionData& primData );
    int do_goto( const InstructionData& primData );   
    int do_calli( const InstructionData& primData );  
    int do_call( const InstructionData& primData );   
    int do_ret( const InstructionData& primData );    
    int do_new( const InstructionData& primData );    
    int do_get( const InstructionData& primData );    
    int do_put( const InstructionData& primData );    
    int do_invoke( const InstructionData& primData ); 
    int do_gettype( const InstructionData& primData );
    int do_enter( const InstructionData& primData );  
    int do_leave( const InstructionData& primData );  
    int do_nop( const InstructionData& primData );    
    int do_dbgbreak( const InstructionData& primData );    
    int do_pack( const InstructionData& primData );   
    int do_upack( const InstructionData& primData );  
    int do_geti( const InstructionData& primData );
    int do_puti( const InstructionData& primData );
    int do_invokei( const InstructionData& primData );
    int do_label( const InstructionData& primData );  

private: // release versions of primitives (absolute no checks!)
    void do_fast_load( const InstructionData& primData );   
    void do_fast_addrof( const InstructionData& primData ); 
    void do_fast_mov( const InstructionData& primData );    
    void do_fast_store( const InstructionData& primData );  
    void do_fast_drop( const InstructionData& primData );   
    void do_fast_dup( const InstructionData& primData );    
    void do_fast_swap( const InstructionData& primData );   
    void do_fast_nz( const InstructionData& primData );     
    void do_fast_convert( const InstructionData& primData );
    void do_fast_goto( const InstructionData& primData );   
    void do_fast_calli( const InstructionData& primData );  
    void do_fast_call( const InstructionData& primData );   
    void do_fast_ret( const InstructionData& primData );    
    void do_fast_new( const InstructionData& primData );    
    void do_fast_get( const InstructionData& primData );    
    void do_fast_put( const InstructionData& primData );    
    void do_fast_invoke( const InstructionData& primData ); 
    void do_fast_gettype( const InstructionData& primData );
    void do_fast_enter( const InstructionData& primData );  
    void do_fast_leave( const InstructionData& primData );  
    void do_fast_nop( const InstructionData& primData );    
    void do_fast_dbgbreak( const InstructionData& primData );    
    void do_fast_pack( const InstructionData& primData );   
    void do_fast_upack( const InstructionData& primData );  
    void do_fast_unary_prim( const InstructionData& primData, _opfn_t *pfn );
    void do_fast_logical_op( const InstructionData& primData, _lopfn_t *pfn );
    void do_fast_if_xx( const InstructionData& primData, _lopfn_t *pfn );
    void do_fast_ifz_nz( const InstructionData& primData, _copfnifx *pfn );
    void do_fast_arith( const InstructionData& primData, _lopfn_t *pfn );
};